#pragma once

#include "Thread.h"
#include "Singleton.h"

class ThreadPool : public Singleton<ThreadPool>
{
	Concurrency::concurrent_queue<std::function<void ()>> mFunctions;
	Concurrency::concurrent_queue<Thread*> mThreads;
	PTP_WORK mWork;

	static void CALLBACK CallbackFun(PTP_CALLBACK_INSTANCE instance, void* ptr, PTP_WORK work) {
		auto queue = reinterpret_cast<Concurrency::concurrent_queue<std::function<void()>>*>(ptr);
		if(queue == nullptr)
			return;

		std::function<void()> fun;
		while(queue->try_pop(fun)) {
			fun();
		}
	}
public:
	ThreadPool() {
		mWork = CreateThreadpoolWork(&ThreadPool::CallbackFun, &mFunctions, nullptr);
	}

	~ThreadPool() {
		Thread* task = NULL;
		while(mThreads.try_pop(task))
			task->setState(Thread::ShouldQuit);

		WaitForThreadpoolWorkCallbacks(mWork, true);
	}

	void shutdown() {
		Thread* task = NULL;
		while(mThreads.try_pop(task))
			task->setState(Thread::ShouldQuit);

		WaitForThreadpoolWorkCallbacks(mWork, true);
	}

	void executeTask(Thread* task) {
		task->setState(Thread::Running);
		mFunctions.push([task, this]() { task->run(); Thread* t = NULL; mThreads.try_pop(t); });
		mThreads.push(task);
		SubmitThreadpoolWork(mWork);
	}

	void executeTask(const std::function<void ()>& fun) {
		mFunctions.push(fun);
		SubmitThreadpoolWork(mWork);
	}
};

#define sThreadPool (ThreadPool::getInstance())